package com.jht.bletool2java.characteristic.ftms;

import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;

import androidx.annotation.Keep;


import com.jht.bletool2java.characteristic.TranslateData;
import com.jht.bletool2java.util.ByteUtil;

import java.math.BigDecimal;

import top.codestudy.annotation_uuid.MyUUID;

//判断处理ok
@Keep
@MyUUID(uuid = "00002acd-0000-1000-8000-00805f9b34fb")
public class TreadmillData implements TranslateData {
    private final String TAG = "TreadmillData";
    private static TreadmillData data;

    private byte[] flags = new byte[2];
    private byte[] instantaneous_speed;
    private byte[] average_speed;
    private byte[] total_distance;
    private byte[] inclination;
    private byte[] ramp_Angle_Setting;
    private byte[] positive_elevation_gain;
    private byte[] negative_elevation_gain;
    private byte[] instantaneous_pace;
    private byte[] average_pace;
    private byte[] total_energy;
    private byte[] energy_per_hour;
    private byte[] energy_per_minute;
    private byte[] heart_rate;
    private byte[] metabolic_equivalent;
    private byte[] elapsed_time;
    private byte[] remaining_time;
    private byte[] force_on_belt;
    private byte[] power_output;

    private boolean InsSpeed = false;
    private boolean avgSpeed = false;
    private boolean totalDistance = false;
    private boolean inclinationAndRampAngleSettingpresent = false;
    private boolean elevationGainpresent = false;
    private boolean instantaneousPace = false;
    private boolean averagePace = false;
    private boolean expendedEnergy = false;
    private boolean heartRate = false;
    private boolean metabolic = false;
    private boolean elapsedTime = false;
    private boolean remainingTime = false;
    private boolean forceOnBeltAndPowerOutput = false;


    private int expect_data_length = 0;
    private boolean isInvalidData = false;

    private byte[] valueData;
    public static TreadmillData getInstance()
    {

        data = new TreadmillData();
        return data;
    }
    public TreadmillData(){}

    public TreadmillData(BluetoothGattCharacteristic characteristic){

        byte[] value = characteristic.getValue();
        valueData = value;

        parseData(value);
        for(int i=0;i<value.length;i++){
            Log.i(TAG, "TreadmillData: i = " + i +"; ox" + Integer.toHexString(ByteUtil.byte1ToInt(value[i])));
        }
    }

    public void parseData(byte[] buffer)
    {
        Log.i(TAG, "parseData: buffer.length " + buffer.length);
        System.arraycopy(buffer, 0, flags, 0, 2);
        checkEachFieldIfSupported();
        parseEachSupportedFeature(buffer);
    }

    private void checkEachFieldIfSupported()
    {

        byte firstbyte = (byte) (flags[0] ^ 0xFE); // 0xFA means all features are supported in 1st byte;
        byte secondbyte =(byte) (flags[1] ^ 0x1F); // 0x1F mean all features are supported in 2nd byte;

        expect_data_length = 2;

        if(isBitZero(firstbyte, 0))
        {
            //instantaneous speed is supported
            InsSpeed = true;
            instantaneous_speed = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "instantaneous speed is supported");
        }else {
            Log.e(TAG,"数据过多！将分包接受！");
        }
        if(isBitZero(firstbyte, 1))
        {
            //average speed is supported
            avgSpeed = true;
            average_speed = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "average speed is supported");
        }

        if(isBitZero(firstbyte, 2))
        {
            //totalDistance is supported
            totalDistance = true;
            total_distance = new byte[3];
            expect_data_length += 3;
            Log.d(TAG,"totalDistance is supported");
        }
        if(isBitZero(firstbyte, 3))
        {
            //inclinationAndRampAngleSettingpresent is supported
            inclinationAndRampAngleSettingpresent = true;
            inclination = new byte[2];
            ramp_Angle_Setting = new byte[2];
            expect_data_length += 4;
            Log.d(TAG,"inclinationAndRampAngleSettingpresent is supported");
        }
        if(isBitZero(firstbyte, 4))
        {
            //elevation Gain present is supported
            elevationGainpresent = true;
            positive_elevation_gain = new byte[2];
            negative_elevation_gain = new byte[2];
            expect_data_length += 4;
            Log.d(TAG,"elevation Gain present is supported");
        }
        if(isBitZero(firstbyte, 5))
        {
            //instantaneousPace is supported
            instantaneousPace = true;
            instantaneous_pace = new byte[1];
            expect_data_length += 1;
            Log.d(TAG,"instantaneousPace is supported");
        }
        if(isBitZero(firstbyte, 6))
        {
            //averagePace is supported
            averagePace = true;
            average_pace = new byte[1];
            expect_data_length += 1;
            Log.d(TAG,"averagePace is supported");
        }
        if(isBitZero(firstbyte, 7))
        {
            // All Energy features are supported
            expendedEnergy = true;
            total_energy = new byte[2];
            energy_per_hour = new byte[2];
            energy_per_minute = new byte[1];
            expect_data_length += 5;
            Log.d(TAG,"expendedEnergy is supported");
        }
        if(isBitZero(secondbyte, 0))
        {
            // heartRate are supported
            heartRate = true;
            heart_rate = new byte[1];
            expect_data_length += 1;
            Log.d(TAG,"heartRate is supported");
        }
        if(isBitZero(secondbyte, 1))
        {
            //metabolic equivalent is supported
            metabolic = true;
            metabolic_equivalent = new byte[1];
            expect_data_length += 1;
            Log.d(TAG,"metabolic is supported");
        }
        if(isBitZero(secondbyte, 2))
        {
            //elapsed time is supported
            elapsedTime = true;
            elapsed_time = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"elapsed time is supported");
        }
        if(isBitZero(secondbyte, 3))
        {
            //remaining time is supported
            remainingTime = true;
            remaining_time = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"remaining time is supported");
        }
        if(isBitZero(secondbyte, 4))
        {
            //Force on Belt is supported
            forceOnBeltAndPowerOutput = true;
            force_on_belt = new byte[2];
            power_output = new byte[2];
            expect_data_length += 4;
            Log.d(TAG,"Force on Belt is supported");
        }

    }

    /**
     *
     * @param value, the xor result, 0 means feature is supported. 1 means feature is not supported
     * @param bit, the bit of value to check
     * @return true bit position is 0;false not 0
     */
    private boolean isBitZero(byte value, int bit)
    {
        int power2 = (int) Math.pow(2, bit);
        int tmp = value & power2;
        return tmp == 0;
    }

    private void parseEachSupportedFeature(byte[] buffer)
    {
        if(buffer.length < expect_data_length)
        {
            isInvalidData = true;
            Log.d(TAG, "buffer length is "+buffer.length);
            Log.d(TAG, "expect data length is "+expect_data_length);
            return;
        }

        int startIndex = 2;
        if(InsSpeed) {
            System.arraycopy(buffer, startIndex, instantaneous_speed, 0, 2);
            startIndex += 2;
        }
        if(avgSpeed) {
            System.arraycopy(buffer, startIndex, average_speed, 0, 2);
            startIndex += 2;
        }

        if(totalDistance) {
            System.arraycopy(buffer, startIndex, total_distance, 0, 3);
            startIndex += 3;
        }

        if(inclinationAndRampAngleSettingpresent) {
            System.arraycopy(buffer, startIndex, inclination, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, ramp_Angle_Setting, 0, 2);
            startIndex += 2;
        }

        if(elevationGainpresent) {
            System.arraycopy(buffer, startIndex, positive_elevation_gain, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, negative_elevation_gain, 0, 2);
            startIndex += 2;
        }

        if(instantaneousPace) {
            System.arraycopy(buffer, startIndex, instantaneous_pace, 0, 1);
            startIndex += 1;
        }

        if(averagePace) {
            System.arraycopy(buffer, startIndex, average_pace, 0, 1);
            startIndex += 1;
        }

        if(expendedEnergy) {
            System.arraycopy(buffer, startIndex, total_energy, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, energy_per_hour, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, energy_per_minute, 0, 1);
            startIndex += 1;
        }

        if(heartRate) {
            System.arraycopy(buffer, startIndex, heart_rate, 0, 1);
            startIndex += 1;
        }

        if(metabolic) {
            System.arraycopy(buffer, startIndex, metabolic_equivalent, 0, 1);
            startIndex += 1;
        }
        if(elapsedTime) {
            System.arraycopy(buffer, startIndex, elapsed_time, 0, 2);
            Log.e(TAG, "parseEachSupportedFeature: elapsed_time ==> 0x" + Integer.toHexString(ByteUtil.bytes2ToInt(elapsed_time,0)));
            startIndex += 2;
        }
        if(remainingTime) {
            System.arraycopy(buffer, startIndex, remaining_time, 0, 2);
            startIndex += 2;
        }

        if(forceOnBeltAndPowerOutput) {
            System.arraycopy(buffer, startIndex, force_on_belt, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, power_output, 0, 2);
        }

    }

    public double getInstantaneousSpeed()
    {
        if(InsSpeed) {
            BigDecimal bigInt = new BigDecimal(ByteUtil.bytes2ToInt(instantaneous_speed, 0));
            BigDecimal bigDouble = new BigDecimal("0.01");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public double getAverageSpeed()
    {
        if(avgSpeed)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.bytes2ToInt(average_speed, 0));
            BigDecimal bigDouble = new BigDecimal("0.01");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public int getTotalDistance()
    {
        if(totalDistance)
        {
            return ByteUtil.byte3ToInt(total_distance, 0);
        }else{
            return -9999;
        }
    }

    public double getinclination()
    {
        if(inclinationAndRampAngleSettingpresent)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.bytes2ToInt(inclination, 0));
            BigDecimal bigDouble = new BigDecimal("0.1");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public double getRamp_Angle_Setting()
    {
        if(inclinationAndRampAngleSettingpresent)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.bytes2ToInt(ramp_Angle_Setting, 0));
            BigDecimal bigDouble = new BigDecimal("0.1");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }


    public double getPositive_elevation_gain()
    {
        if(elevationGainpresent)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.bytes2ToInt(positive_elevation_gain, 0));
            BigDecimal bigDouble = new BigDecimal("0.1");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public double getNegative_elevation_gain()
    {
        if(elevationGainpresent)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.bytes2ToInt(negative_elevation_gain, 0));
            BigDecimal bigDouble = new BigDecimal("0.1");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public double getInstantaneousPace()
    {
        if(instantaneousPace)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.byte1ToInt(instantaneous_pace[0]));
            BigDecimal bigDouble = new BigDecimal("0.1");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public double getAverage_pace()
    {
        if(averagePace)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.byte1ToInt(average_pace[0]));
            BigDecimal bigDouble = new BigDecimal("0.1");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public int getTotalEnergy()
    {
        if(expendedEnergy)
        {
            return ByteUtil.bytes2ToInt(total_energy, 0);
        }else{
            return -9999;
        }
    }

    public int getEnergyPerHour()
    {
        if(expendedEnergy)
        {
            return ByteUtil.bytes2ToInt(energy_per_hour, 0);
        }else{
            return -9999;
        }
    }

    public int getEnergyPerMinute()
    {
        if(expendedEnergy)
        {
            return ByteUtil.byte1ToInt(energy_per_minute[0]);
        }else{
            return -9999;
        }
    }

    public int getHeartRate()
    {
        if(heartRate)
        {
            return ByteUtil.byte1ToInt(heart_rate[0]);
        }else{
            return -9999;
        }
    }

    public double getMetabolicEquivalent()
    {
        if(metabolic)
        {
            BigDecimal bigInt = new BigDecimal(ByteUtil.byte1ToInt(metabolic_equivalent[0]));
            BigDecimal bigDouble = new BigDecimal("0.1");
            return bigInt.multiply(bigDouble).doubleValue();
        }else{
            return -9999;
        }
    }

    public int getElapedTime()
    {
        if(elapsedTime)
        {
            return ByteUtil.bytes2ToInt(elapsed_time, 0);
        }else{
            return -9999;
        }
    }

    public int getRemainingTime()
    {
        if(remainingTime)
        {
            return ByteUtil.bytes2ToInt(remaining_time, 0);
        }else{
            return -9999;
        }
    }

    public int getforce_on_belt()
    {
        if(forceOnBeltAndPowerOutput)
        {
            return ByteUtil.bytes2ToInt(force_on_belt, 0);
        }else{
            return -9999;
        }
    }

    public int getPower_output()
    {
        if(forceOnBeltAndPowerOutput)
        {
            return ByteUtil.bytes2ToInt(power_output, 0);
        }else{
            return -9999;
        }
    }

    @Override
    public String convert2String() {

        return "instant speed : " +   getInstantaneousSpeed()
                + " km/h ;\n average speed : " +   getAverageSpeed()
                + " km/h ;\n Ramp_Angle_Setting : " +   getRamp_Angle_Setting()
                + " degree ;\n total distance : " +   getTotalDistance()
                + " m ;\n TotalEnergy : " +   getTotalEnergy()
                + " Calorie ;\n inclination : " +   getinclination()
                + " percentage ;\n HeartRate : " +   getHeartRate()
                + " bpm ;\n elapsed time : " +   getElapedTime()
                + " second ;\n Average_pace : " +   getAverage_pace()
                + " kilometre_per_minute ;\n InstantaneousPace : " +   getInstantaneousPace()
                + " kilometre_per_minute; \n Positive_elevation_gain : " +   getPositive_elevation_gain()
                + " metre ;\n Negative_elevation_gain :  " +   getNegative_elevation_gain()
                + " metre ;\n Energy Per Hour : " + getEnergyPerHour()
                + " ;\n Energy Per Minute : " + getEnergyPerMinute()
                + " ;\n Metabolic Equivalent : " + getMetabolicEquivalent()
                + " ;\n  Remaining Time : " + getRemainingTime()
                + " ;\n  force_on_belt : " + getforce_on_belt()
                + " ;\n  Power_output : " + getPower_output()+";\n ";

    }

    @Override
    public String merge(TranslateData translateData) {
        TreadmillData temp = (TreadmillData)translateData;
        if(temp.InsSpeed) {
            this.instantaneous_speed=temp.instantaneous_speed;
            this.InsSpeed = true;
        }
        if(temp.avgSpeed) {
            this.average_speed=temp.average_speed;
            this.avgSpeed = true;
        }

        if(temp.totalDistance) {
            this.total_distance=temp.total_distance;
            this.totalDistance = true;
        }

        if(temp.inclinationAndRampAngleSettingpresent) {
            this.inclination=temp.inclination;
            this.ramp_Angle_Setting=temp.ramp_Angle_Setting;
            this.inclinationAndRampAngleSettingpresent = true;
        }

        if(temp.elevationGainpresent) {
            this.positive_elevation_gain=temp.positive_elevation_gain;

            this.negative_elevation_gain=temp.negative_elevation_gain;
            this.elevationGainpresent = true;
        }

        if(temp.instantaneousPace) {
            this.instantaneous_pace=temp.instantaneous_pace;
            this.instantaneousPace = true;
        }

        if(temp.averagePace) {
            this.average_pace=temp.average_pace;
            this.averagePace = true;
        }

        if(temp.expendedEnergy) {
            this.total_energy=temp.total_energy;

            this.energy_per_hour=temp.energy_per_hour;

            this.energy_per_minute=temp.energy_per_minute;
            this.expendedEnergy = true;
        }

        if(temp.heartRate) {
            this.heart_rate=temp.heart_rate;
            this.heartRate = true;
        }

        if(temp.metabolic) {
            this.metabolic_equivalent=temp.metabolic_equivalent;
            this.metabolic = true;
        }
        if(temp.elapsedTime) {
            this.elapsed_time=temp.elapsed_time;
            this.elapsedTime = true;

        }
        if(temp.remainingTime) {
            this.remaining_time=temp.remaining_time;
            this.remainingTime = true;
        }

        if(temp.forceOnBeltAndPowerOutput) {
            this.force_on_belt=temp.force_on_belt;

            this.power_output=temp.power_output;
            this.forceOnBeltAndPowerOutput = true;
        }

        return null;
    }

    @Override
    public boolean hasMoreData() {
        return !InsSpeed;
    }

    @Override
    public byte[] getData() {
        return valueData;
    }

    @Override
    public boolean isInvalidData() {
        return isInvalidData;
    }

    @Override
    public void parseData(BluetoothGattCharacteristic characteristic, byte[] value) {
        init();
        isInvalidData = (value == null || value.length < 2);
        if (value!=null){
            if (isInvalidData){
                this.valueData = value;
            }else {
                this.valueData = value;
                parseData(value);
                for(int i=0;i<value.length;i++){
                    Log.i(TAG, "TreadmillData: i = " + i +"; ox" + Integer.toHexString(ByteUtil.byte1ToInt(value[i])));
                }
            }
        }
    }

    private void init(){
        InsSpeed = false;
        avgSpeed = false;
        totalDistance = false;
        inclinationAndRampAngleSettingpresent = false;
        elevationGainpresent = false;
        instantaneousPace = false;
        averagePace = false;
        expendedEnergy = false;
        heartRate = false;
        metabolic = false;
        elapsedTime = false;
        remainingTime = false;
        forceOnBeltAndPowerOutput = false;
        expect_data_length = 0;
        isInvalidData = false;

        this.valueData = null;
    }
}
